home *** CD-ROM | disk | FTP | other *** search
/ HPAVC / HPAVC CD-ROM.iso / pc / 2924.ZIP / DMLXREF.ARC / NUM.IMP < prev    next >
Encoding:
Text File  |  1988-12-20  |  28.9 KB  |  1,027 lines

  1. (**************************************************************************)
  2. (*                                                                        *)
  3. (*          1)  General Numeric Formatting And Conversion                 *)
  4. (*                                                                        *)
  5. (*                                                                        *)
  6. (**************************************************************************)
  7.  
  8. CONST
  9.  
  10.   S_INT = 1;  (* flag for INTEGER arg to Num2S *)
  11.   L_INT = 2;  (* flag for LONGINT arg to Num2S *)
  12.  
  13. TYPE
  14.   VarType = BYTE;  (* values: 1=INTEGER, 2=LONGINT   see CONST S_INT & L_INT *)
  15.  
  16. {.PA}
  17.  
  18.  
  19. {=- NumLow1_ConvertMask -=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=}
  20.  
  21. FUNCTION NumLow1_ConvertMask (Mask     : STRING;
  22.                               Negative : BOOLEAN;
  23.                               RealMask : BOOLEAN): STRING;
  24.  
  25. {RealMask must be TRUE to allow the decimal character in the mask, and it must
  26.  be set to FALSE to allow H (Hexidecimal) or B (Binary) in the mask.}
  27.  
  28. {Destroys local copy of Mask}
  29.  
  30. VAR
  31.   MaskLength  : INTEGER;
  32.   Ctr         : INTEGER;
  33.   ConvertChar : CHAR;
  34.   Error       : BOOLEAN;
  35.   DecimalPos  : INTEGER;
  36.  
  37.   PROCEDURE CaseStatement (Selector : INTEGER);
  38.   BEGIN
  39.     CASE Mask [Selector] OF
  40.       '(',
  41.       ')',
  42.       '-' : IF NOT Negative THEN Mask [Selector] := ' ';
  43.       '+' : IF Negative     THEN Mask [Selector] := '-';
  44.   'H','h',
  45.   'B','b' : IF RealMask THEN Error := TRUE ELSE Mask [Selector] := ConvertChar;
  46.       '#' : Mask [Selector] := ConvertChar;
  47.       '*' : IF ConvertChar = '#' THEN ConvertChar     := '*'
  48.                                  ELSE Mask [Selector] := ConvertChar;
  49.       '@' : BEGIN
  50.               ConvertChar := '0';
  51.               Mask [Selector]  := '0';
  52.               END;
  53.       '.',
  54.       ' ',
  55.       ',',
  56.       '$' : ; {Allow any number and placement of these chars!}
  57.       ELSE Error := TRUE;
  58.       END; {Case}
  59.   END;
  60.  
  61. BEGIN
  62.   MaskLength  := LENGTH (Mask);
  63.   Error       := FALSE;
  64.  
  65.   DecimalPos := POS ('.', Mask);
  66.   Error := (NOT RealMask) AND (DecimalPos <> 0);
  67.   IF DecimalPos = 0 THEN DecimalPos := LENGTH (Mask);
  68.  
  69.   IF NOT Error THEN BEGIN
  70.     ConvertChar := '#';
  71.     FOR Ctr := 1 TO DecimalPos DO
  72.       CaseStatement (Ctr);
  73.  
  74.     ConvertChar := '#';
  75.     FOR Ctr := LENGTH (Mask) DOWNTO DecimalPos+1 DO
  76.       CaseStatement (Ctr);
  77.     END;
  78.  
  79.   IF Error THEN NumLow1_ConvertMask := StrFill ('?', MaskLength)
  80.            ELSE NumLow1_ConvertMask := Mask;
  81. END;
  82.  
  83. {=- NumLow1_ApplyMask -=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=}
  84.  
  85. FUNCTION NumLow1_ApplyMask (NumStr,
  86.                            MaskStr  : STRING;
  87.                            Negative : BOOLEAN;
  88.                            RealMask : BOOLEAN) : STRING;
  89.  
  90. {RealMask must be true to properly line up decimal points.  If it is false
  91.  decimal points are simply ignored (i.e. printed).}
  92.  
  93. {destroys the local copy of MaskStr!}
  94.  
  95. VAR
  96.   Found     : BOOLEAN;
  97.   MaskCtr   : INTEGER;
  98.   MaskMax   : INTEGER;
  99.   MaskDeci  : INTEGER;
  100.   NumCtr    : INTEGER;
  101.   NumMax    : INTEGER;
  102.   NumDeciStr  : STRING;
  103.   OverFlow  : BOOLEAN;
  104.   SignFound : BOOLEAN;
  105.   BothFound : BOOLEAN;
  106.  
  107. BEGIN
  108.   OverFlow := FALSE;
  109.   MaskMax  := LENGTH (MaskStr);
  110.   NumMax   := LENGTH (NumStr);
  111.  
  112.   IF MaskStr [1] <> '?' THEN BEGIN
  113.   MaskDeci := LENGTH (MaskStr);
  114.  
  115.   {If a real number, but an integer mask type}
  116.   IF POS('.', MaskStr) = 0 THEN RealMask := FALSE;
  117.  
  118.     {Copy the number into the mask Real only}
  119.     IF RealMask THEN BEGIN
  120.       NumDeciStr := '.'+StrField (NumStr,'.',2);
  121.       NumStr     := StrField (NumStr,'.',1);
  122.       NumMax     := LENGTH (NumStr);
  123.  
  124.       {Strip off trailing zeros}
  125.       NumCtr   := LENGTH (NumDeciStr);
  126.       WHILE (NumDeciStr [NumCtr] = '0') AND (NumCtr > 1) DO
  127.         NumCtr := PRED (NumCtr);
  128.       NumDeciStr [0] := CHR (NumCtr);
  129.  
  130.       {Fill mask after the decimal point}
  131.       MaskDeci   := POS ('.', MaskStr);
  132.       IF MaskDeci > 0 THEN BEGIN
  133.         MaskDeci := PRED (MaskDeci);  {Ignore the actual period char}
  134.         MaskCtr  := MaskDeci+2;
  135.         FOR NumCtr := 2 TO LENGTH (NumDeciStr) DO BEGIN
  136.           Found  := FALSE;
  137.           REPEAT
  138.             IF NOT(MaskStr[MaskCtr] IN ['#','*','0']) THEN
  139.               MaskCtr := SUCC(MaskCtr)
  140.             ELSE
  141.               Found := TRUE;
  142.           UNTIL Found;
  143.           MaskStr [MaskCtr] := NumDeciStr [NumCtr];
  144.           MaskCtr := SUCC(MaskCtr);
  145.           END;
  146.  
  147.         {Clean up trailing mask characters}
  148.         MaskCtr := POS ('.', MaskStr);
  149.         MaskMax := LENGTH (MaskStr);
  150.         IF MaskCtr > 0 THEN BEGIN
  151.           FOR MaskCtr := MaskCtr TO PRED(MaskMax) DO BEGIN
  152.             IF MaskStr [MaskCtr] = '#' THEN MaskStr [MaskCtr] := ' ';
  153.             IF (MaskStr [MaskCtr] = '.') AND (MaskStr [MaskCtr+1] = '#') THEN
  154.               MaskStr [MaskCtr] := ' ';
  155.             IF MaskStr [MaskCtr] = ',' THEN
  156.               IF MaskStr [MaskCtr+1] IN ['*','0'] THEN
  157.                 MaskStr [MaskCtr] := MaskStr [MaskCtr+1]
  158.               ELSE
  159.                 MaskStr [MaskCtr] := ' ';
  160.             END;
  161.           IF MaskStr [MaskMax] = '#' THEN MaskStr [MaskMax] := ' ';
  162.           END;
  163.         END;
  164.       END;
  165.  
  166.     {Copy Number into the Mask integer and real}
  167.  
  168.     MaskCtr := MaskDeci;
  169.     FOR NumCtr := NumMax DOWNTO 1 DO BEGIN
  170.       IF MaskCtr > 0 THEN BEGIN
  171.         Found := FALSE;
  172.         REPEAT
  173.           IF NOT(MaskStr[MaskCtr] IN ['#','*','0']) THEN
  174.             MaskCtr := PRED(MaskCtr)
  175.           ELSE
  176.             Found := TRUE;
  177.           UNTIL Found OR (MaskCtr = 0);
  178.         OverFlow := (MaskCtr = 0);
  179.         END
  180.       ELSE OverFlow := TRUE;
  181.  
  182.       IF MaskCtr > 0 THEN BEGIN
  183.         MaskStr [MaskCtr] := NumStr [NumCtr];
  184.         MaskCtr := PRED(MaskCtr);
  185.         END;
  186.       END;
  187.  
  188.     {Clean up leading mask characters}
  189.     IF NOT OverFlow THEN BEGIN
  190.       FOR MaskCtr := MaskCtr DOWNTO 2 DO BEGIN
  191.         IF MaskStr [MaskCtr] = '#' THEN MaskStr [MaskCtr] := ' ';
  192.         IF MaskStr [MaskCtr] = ',' THEN
  193.           IF MaskStr [MaskCtr-1] IN ['*','0'] THEN
  194.             MaskStr [MaskCtr] := MaskStr [MaskCtr-1]
  195.           ELSE
  196.             MaskStr [MaskCtr] := ' ';
  197.         END;
  198.       IF MaskStr [1] = '#' THEN MaskStr [1] := ' ';
  199.  
  200.       {Move the sign flag next to the number}
  201.       SignFound := (MaskStr [1] IN ['-','+','(']) OR
  202.                    (MaskStr [LENGTH(MaskStr)] IN ['-','+',')']);
  203.       BothFound := (POS('$-',MaskStr) > 0) OR (POS('-$',MaskStr) > 0);
  204.       OverFlow  := (LENGTH (MaskStr) = 1);
  205.       {Move leading sign}
  206.       FOR MaskCtr := 1 TO MaskMax-1 DO
  207.         IF (MaskStr [MaskCtr] IN ['-','+','(','$']) AND
  208.            (MaskStr [MaskCtr+WORD(BothFound)+1] = ' ') THEN BEGIN
  209.           IF MaskStr[MaskCtr] <> '$' THEN SignFound := TRUE;
  210.           MOVE(MaskStr[MaskCtr],MaskStr[MaskCtr+1],WORD(BothFound)+1);
  211.           MaskStr [MaskCtr]   := ' ';
  212.           END;
  213.       {Move trailing sign flag}
  214.       FOR MaskCtr := LENGTH (MaskStr) DOWNTO 2 DO
  215.         IF (MaskStr [MaskCtr] = ')') AND (MaskStr [MaskCtr-1] = ' ') THEN BEGIN
  216.           MaskStr [MaskCtr-1] := ')';
  217.           MaskStr [MaskCtr]   := ' ';
  218.           END;
  219.       OverFlow := (Negative AND (NOT SignFound));
  220.       END;
  221.  
  222.     END;
  223.  
  224.   {Assign value to the Function -- check for overflow}
  225.   IF OverFlow THEN NumLow1_ApplyMask := StrFill ('*', MaskMax)
  226.               ELSE NumLow1_ApplyMask := MaskStr;
  227. END;
  228.  
  229. (******************************************************************)
  230.  
  231. { Vtype will either be 1 for INTEGER or 2 for LONGINT       }
  232. {       currently examined by the hex conversion routine to }
  233. {       determine the number of leading zeros to pad to the }
  234. {       output hex string                                   }
  235.  
  236. FUNCTION Num2S (Vtype : VarType;
  237.                 Num : LONGINT; Mask : STRING) : STRING;
  238.  
  239. VAR
  240.   Negative  : BOOLEAN;
  241.   Base16    : BOOLEAN;
  242.  
  243. {=- Num2S -=- MaskBase =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=}
  244.  
  245.   FUNCTION MaskBase : BYTE;
  246.   {Returns the base specified in the Mask string.  Valid bases are:
  247.  
  248.      # - Base 10 - returns value 10
  249.      H - Base 16 - returns value 16
  250.      O - Base  8 - returns value  8
  251.      B - Base  2 - returns value  2
  252.  
  253.   If an error is found in specifying a base (i.e. both #'s and H's) the
  254.   returned value is zero.  Also if no base character is found the returned
  255.   code is zero. }
  256.  
  257.   VAR
  258.     Ctr  : BYTE;
  259.     Base : BYTE;
  260.     StarOrAtFound : BOOLEAN;
  261.  
  262.   BEGIN {MaskBase}
  263.     Base := 255;
  264.     StarOrAtFound := FALSE;
  265.  
  266.     FOR Ctr := 1 TO LENGTH (Mask) DO
  267.       CASE Mask [Ctr] OF
  268.         '#'     : IF (Base = 255) OR (Base = 10) THEN Base := 10 ELSE Base := 0;
  269.         'H','h' : IF (Base = 255) OR (Base = 16) THEN Base := 16 ELSE Base := 0;
  270.         'O','o' : IF (Base = 255) OR (Base =  8) THEN Base :=  8 ELSE Base := 0;
  271.         'B','b' : IF (Base = 255) OR (Base =  2) THEN Base :=  2 ELSE Base := 0;
  272.         '@','*' : StarOrAtFound := TRUE;
  273.         ELSE    ;
  274.         END; {CASE}
  275.  
  276.     {The Star and @ chars alone are base 10, otherwise they are any base}
  277.     IF (Base = 255) AND StarOrAtFound THEN Base := 10;
  278.     IF (Base = 255) THEN Base := 0;
  279.  
  280.     Base16 := (Base = 16);
  281.     MaskBase := Base;
  282.   END; {MaskBase}
  283.  
  284. {=- Num2S -=- ConvertToBase -=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=}
  285.  
  286.   FUNCTION ConvertToBase : STRING;
  287.   CONST
  288.     Hex : ARRAY [0..15] OF CHAR = '0123456789ABCDEF';
  289.  
  290.   VAR
  291.     BaseStr : STRING;
  292.     HiWord,               (* high order 2 bytes of longint *)
  293.     LoWord  : LONGINT;    (* low  order 2 bytes of longint *)
  294.     Size    : INTEGER;
  295.     Ctr     : INTEGER;
  296.  
  297.   BEGIN {ConvertToBase}
  298.     Negative := FALSE;
  299.     BaseStr  := '';
  300.     Size := LENGTH (Mask);
  301.  
  302.     HiWord := Num shr 16;            (* used for hex and binary conversions *)
  303.     LoWord := (Num shl 16) shr 16;   (*                                     *)
  304.  
  305.     {Convert the number into the correct base}
  306.     CASE MaskBase OF
  307.       10 : BEGIN
  308.              Negative := (Num < 0);
  309.              Num      := ABS (Num);
  310.              STR (Num:Size, BaseStr);
  311.            END;
  312.  
  313.       16 : BEGIN   (* handles LONGINTs *)
  314.  
  315.              (* BaseStr will have a length of 8 *)
  316.  
  317.              BaseStr := Hex[HI(HiWord) SHR 4] + Hex[HI(HiWord) AND $0F] +
  318.                         Hex[LO(HiWord) SHR 4] + Hex[LO(HiWord) AND $0F] +
  319.                         Hex[HI(LoWord) SHR 4] + Hex[HI(LoWord) AND $0F] +
  320.                         Hex[LO(LoWord) SHR 4] + Hex[LO(LoWord) AND $0F];
  321.  
  322.              (* delete the 4 leading 0's for INTEGERs *)
  323.              if VType = S_INT then Delete(BaseStr,1,4);
  324.  
  325.              (* delete leading 0's if longer than mask *)
  326.              WHILE (Length(BaseStr) > Size) and (BaseStr[1] = '0') do
  327.                 Delete(BaseStr,1,1);
  328.  
  329.            END;
  330.  
  331.        2 : BEGIN
  332.              (* convert the first 2 bytes *)
  333.              FOR Ctr := 0 TO 15 DO
  334.                BaseStr := Hex[(LoWord SHR Ctr) AND $01] + BaseStr;
  335.  
  336.              (* convert the next 2 bytes *)
  337.              FOR Ctr := 0 TO 15 DO
  338.                BaseStr := Hex[(HiWord SHR Ctr) AND $01] + BaseStr;
  339.  
  340.              (* delete the 16 leading 0's for INTEGERs *)
  341.              if VType = S_INT then Delete(BaseStr,1,16);
  342.  
  343.              (* delete leading 0's if longer then mask *)
  344.              WHILE (Length(BaseStr) > Size) and (BaseStr[1] = '0') do
  345.                 Delete(BaseStr,1,1);
  346.  
  347.            END;
  348.  
  349.       ELSE BaseStr := StrFill ('?', Size);
  350.       END;
  351.     ConvertToBase := Strip(BaseStr,S_Leading+S_Trailing);
  352.   END; {ConvertToBase}
  353.  
  354. {=- Num2S -=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=}
  355.  
  356. VAR
  357.   TempStr  : STRING;
  358.   Size     : INTEGER;
  359.   TempMask : STRING;
  360.  
  361. BEGIN
  362.   Size     := LENGTH (Mask);
  363.   TempStr  := ConvertToBase;
  364.   IF (Num = 0) AND (NOT Base16) THEN TempStr := '';
  365.   TempMask := NumLow1_ConvertMask (Mask, Negative, FALSE);
  366.   TempStr  := RJS (NumLow1_ApplyMask (TempStr, TempMask, Negative, FALSE), Size);
  367.   Num2S      := TempStr;
  368.  
  369. END; (* Num2S *)
  370.  
  371. (******************************************************************)
  372.  
  373. FUNCTION L2S (Num : LONGINT; Mask : STRING) : STRING;
  374.  
  375.    begin
  376.       L2S := Num2S(L_INT,Num,Mask);
  377.    end; (* L2S *)
  378.  
  379. (******************************************************************)
  380.  
  381. FUNCTION I2S(Num : INTEGER; Mask : STRING) : STRING;
  382.  
  383.    begin
  384.       I2S := Num2S(S_INT,Num,Mask);
  385.    end; (* I2S *)
  386.  
  387. (******************************************************************)
  388.  
  389. FUNCTION W2S(Num : WORD; Mask : STRING) : STRING;
  390.  
  391.    begin
  392.       W2S := Num2S(S_INT,Num,Mask);
  393.    end; (* W2S *)
  394.  
  395. (******************************************************************)
  396.  
  397. FUNCTION Real2S(Num : DOUBLE; Mask : STRING) : STRING;
  398. VAR
  399.   TempStr  : STRING;
  400.   Size     : INTEGER;
  401.   Places   : INTEGER;
  402.   Ctr      : INTEGER;
  403.   Negative : BOOLEAN;
  404.   TempMask : STRING;
  405.  
  406. BEGIN
  407.   Negative := (Num < 0);
  408.   Num      := ABS (Num);
  409.   Size     := LENGTH (Mask);
  410.   TempMask := NumLow1_ConvertMask (Mask, Negative, TRUE);
  411.  
  412.   {Count the number of places after the decimal point}
  413.   Places := 0;
  414.   Ctr := POS ('.', TempMask);
  415.   IF Ctr > 0 THEN
  416.     FOR Ctr := Ctr TO LENGTH (TempMask) DO
  417.       IF TempMask [Ctr] IN ['#','*','0'] THEN
  418.         Places := SUCC(Places);
  419.   IF Places > 20 THEN Places := 20;
  420.  
  421.   STR (Num:Size:Places, TempStr);
  422.   TempStr  := Strip (TempStr, S_Leading+S_Trailing);
  423.   IF Num = 0 THEN TempStr := '';
  424.   TempStr  := RJS (NumLow1_ApplyMask (TempStr, TempMask, Negative, TRUE), Size);
  425.   Real2S      := TempStr;
  426. END;
  427.  
  428. (******************************************************************)
  429.  
  430. FUNCTION R2S(Num : REAL; Mask : STRING) : STRING;
  431.  
  432.    begin
  433.       R2S := Real2S(Num,Mask);
  434.    end; (* R2S *)
  435.  
  436. (******************************************************************)
  437.  
  438. FUNCTION D2S(Num : DOUBLE; Mask : STRING) : STRING;
  439.  
  440.    begin
  441.       D2S := Real2S(Num,Mask);
  442.    end; (* D2S *)
  443.  
  444. (******************************************************************)
  445.  
  446. {.PA}
  447.  
  448. FUNCTION S2Real (Source : STRING) : DOUBLE;
  449.  
  450. VAR
  451.   Ctr    : INTEGER;
  452.   Code   : INTEGER;
  453.   NumStr : STRING;
  454.   Num    : DOUBLE;
  455.   Negative : BOOLEAN;
  456.  
  457. BEGIN
  458.   Negative := FALSE;
  459.   NumStr := '';
  460.   {Strip out any non-numerals - set the negative flag if necessary}
  461.   FOR Ctr := 1 TO LENGTH (Source) DO
  462.     IF Source [Ctr] IN ['0'..'9','.'] THEN
  463.       NumStr := NumStr + Source [Ctr]
  464.     ELSE IF Source [Ctr] IN ['-','('] THEN
  465.       Negative := TRUE;
  466.  
  467.   {Force the null string to zero}
  468.   IF NumStr = '' THEN NumStr := '0';
  469.  
  470.   {Force the correct sign}
  471.   IF Negative THEN NumStr := '-' + NumStr;
  472.  
  473.   IF NumStr[LENGTH(NumStr)] IN ['.','-'] THEN NumStr := NumStr + '0';
  474.   VAL (NumStr, Num, Code);
  475.   IF Code <> 0 THEN BEGIN
  476.     ScrErrMsg ('Tried to convert "'+NumStr+'", error in character #'+I2S(Code,'####@'));
  477.     S2Real := 0;   { Abend (2, NIL); }
  478.     END
  479.   ELSE
  480.     S2Real := Num;
  481. END;
  482.  
  483. (******************************************************************)
  484.  
  485. FUNCTION S2R (Source : STRING) : REAL;
  486.  
  487.    begin
  488.       S2R := S2Real(Source);
  489.    end; (* S2R *)
  490.  
  491. (******************************************************************)
  492.  
  493. FUNCTION S2D (Source : STRING) : DOUBLE;
  494.  
  495.    begin
  496.       S2D := S2Real(Source);
  497.    end; (* S2D *)
  498.  
  499. (******************************************************************)
  500.  
  501. FUNCTION StrNumTest (Fld : STRING) : StrNumType;
  502.  
  503. VAR
  504.   Ctr     : WORD;
  505.   TempStr : STRING;
  506.   StrNum  : StrNumType;
  507.  
  508. BEGIN
  509.   StrNum := StrNonNumeric;
  510.   Ctr := 1;
  511.   WHILE (Ctr <= LENGTH(Fld)) AND (StrNum <> StrNonZero) DO BEGIN
  512.     IF Fld[Ctr] IN ['0'..'9'] THEN IF Fld[Ctr] <> '0'
  513.       THEN StrNum := StrNonZero
  514.       ELSE StrNum := StrZero;
  515.     INC(Ctr);
  516.     END;
  517.   StrNumTest := StrNum;
  518. END;
  519.  
  520. FUNCTION S2X (Source : STRING; Min, Max : LONGINT) : LONGINT;
  521.  
  522.   FUNCTION Power2 (Pow : INTEGER) : LONGINT;
  523.   {integers can never overflow in turbo they just wrap around!}
  524.   VAR
  525.     Ctr : INTEGER;
  526.     Num : LONGINT;
  527.   BEGIN
  528.     Num := 1;
  529.     FOR Ctr := 1 TO Pow DO
  530.       Num := Num * 2;
  531.     Power2 := Num;
  532.   END;
  533.  
  534.  
  535. CONST
  536.   Unknown     = 0;
  537.   Binary      = 1;
  538.   Hexidecimal = 2;
  539.   Decimal     = 3;
  540.  
  541. VAR
  542.   SourceLen : INTEGER;
  543.   Ctr       : INTEGER;
  544.   Code      : INTEGER;
  545.   NumStr    : STRING;
  546.   Num       : LONGINT;
  547.   Negative  : BOOLEAN;
  548.   Base      : Unknown..Decimal;
  549.  
  550. BEGIN
  551.   Base      := Unknown;
  552.   Negative  := FALSE;
  553.   NumStr    := '';
  554.   Num       := 0;
  555.   Code      := 0;
  556.   SourceLen := LENGTH (Source);
  557.  
  558.   {Determine base (leading or trailing H) and set negative flag}
  559.   FOR Ctr := 1 TO SourceLen DO
  560.     IF Source [Ctr] IN ['-','('] THEN
  561.       Negative := TRUE
  562.     ELSE IF (Source [Ctr] IN ['H','h']) AND (Base = Unknown) THEN
  563.       Base := Hexidecimal;
  564.  
  565.   {If base is unknown, see if it is base 10 or base 2 (Binary)}
  566.   IF Base = Unknown THEN
  567.     FOR Ctr := 1 TO SourceLen DO
  568.       IF (Source [Ctr] IN ['B','b']) AND (Base = Unknown) THEN
  569.       Base := Binary;
  570.  
  571.   {If base is still unknown, then it is base 10}
  572.   IF Base = Unknown THEN Base := Decimal;
  573.  
  574.   {Strip out any non-numerals}
  575.   CASE Base OF
  576.     Decimal     : BEGIN
  577.                     Ctr := 1;
  578.                     WHILE (Ctr <= SourceLen) AND (Source[Ctr] <> '.') DO BEGIN
  579.                       IF Source [Ctr] IN ['0'..'9'] THEN NumStr := NumStr + Source[Ctr];
  580.                       INC(Ctr);
  581.                       END;
  582.                     END;
  583.  
  584.     Hexidecimal : BEGIN
  585.                     NumStr := '$' + NumStr;
  586.                     FOR Ctr := 1 TO SourceLen DO
  587.                       IF Source [Ctr] IN ['0'..'9','A'..'F','a'..'f'] THEN
  588.                         NumStr := NumStr + Source [Ctr];
  589.                     END;
  590.  
  591.     Binary      : BEGIN
  592.                     FOR Ctr := 1 TO SourceLen DO
  593.                       IF Source [Ctr] IN ['0'..'1'] THEN
  594.                         NumStr := NumStr + Source [Ctr];
  595.  
  596.                     FOR Ctr := LENGTH (NumStr) DOWNTO 1 DO
  597.                       IF NumStr [Ctr] = '1' THEN
  598.                         Num := Num + Power2(LENGTH(NumStr)-Ctr);
  599.                     END;
  600.     END; {CASE}
  601.  
  602.   {Force the null string to zero}
  603.   IF NumStr = '' THEN NumStr := '0';
  604.  
  605.   {Force the correct sign}
  606.   IF Negative THEN NumStr := '-' + NumStr;
  607.   IF NumStr[LENGTH(NumStr)] IN ['-'] THEN NumStr := NumStr + '0';
  608.  
  609.   IF Base <> Binary THEN
  610.     VAL (NumStr, Num, Code);
  611.  
  612.   IF Code <> 0 THEN BEGIN
  613.     ScrErrMsg ('Tried to convert "'+NumStr+'", error in character #'+I2S(Code,'####@'));
  614.     S2X := 0;    { Abend (2, NIL); }
  615.     END
  616.   ELSE IF (Num >= Min) AND (Num <= Max)
  617.     THEN S2X := Num
  618.     ELSE S2X := 0;
  619.  
  620. END;  (* S2X *)
  621.  
  622. (******************************************************************)
  623.  
  624. FUNCTION S2I(Source : STRING) : INTEGER;
  625.  
  626.    begin
  627.       S2I := S2X(Source,-MAXINT-1,MAXINT);
  628.    end; (* S2I *)
  629.  
  630. FUNCTION S2W(Source : STRING) : WORD;
  631.  
  632.    begin
  633.       S2W := S2X(Source,0,MAXINT*2+1);
  634.    end; (* S2W *)
  635.  
  636.  
  637. FUNCTION S2L(Source : STRING) : LONGINT;
  638.  
  639.    begin
  640.       S2L := S2X(Source,-MAXLONGINT-1,MAXLONGINT);
  641.    end; (* S2I *)
  642. (******************************************************************)
  643.  
  644. FUNCTION B2S (Flag : BOOLEAN) : STRING;
  645.  
  646. BEGIN
  647.   IF Flag
  648.     THEN B2S := 'TRUE'
  649.     ELSE B2S := 'FALSE';
  650. END;
  651.  
  652. {.PA}
  653.  
  654. (**************************************************************************)
  655. (*                                                                        *)
  656. (*          2)  Date and Time Formatting And Conversion                   *)
  657. (*                                                                        *)
  658. (*                                                                        *)
  659. (**************************************************************************)
  660.  
  661. CONST
  662.   MonthName   : ARRAY [1..12] OF DateTimeStr =
  663.                 ('January','February','March','April','May','June','July',
  664.                  'August','September','October','November','December');
  665.  
  666.   WeekdayName : ARRAY [1..7] OF DateTimeStr =
  667.                 ('Sunday','Monday','Tuesday','Wednesday','Thursday','Friday',
  668.                  'Saturday');
  669.  
  670. {.PA}
  671.  
  672. FUNCTION NumTh (Num : INTEGER) : STRING;
  673. VAR
  674.   TempStr : STRING;
  675.   AddChar : STRING [2];
  676.  
  677. BEGIN
  678.   TempStr := I2S(Num, '####@');
  679.   IF NOT ((TempStr[4] = '1') AND (TempStr[5] IN ['1'..'3'])) THEN
  680.     CASE TempStr [5] OF
  681.       '1' : AddChar := 'st';
  682.       '2' : AddChar := 'nd';
  683.       '3' : AddChar := 'rd';
  684.       ELSE  AddChar := 'th';
  685.       END {CASE}
  686.   ELSE
  687.     AddChar := 'th';
  688.  
  689.   NumTh := TempStr + AddChar;
  690. END;
  691.  
  692. PROCEDURE Date2R (VAR JulSec : REAL; DateTime : T_DateTime);
  693. VAR
  694.   MO,DA,YR : REAL;
  695.   JUL,Sec  : REAL;
  696.  
  697. BEGIN
  698.   WITH DateTime DO BEGIN
  699.     MO := Month - 3;
  700.     DA := Day;
  701.  
  702.     IF YEAR < 100 THEN YR := Year + 1900 - 1840
  703.                   ELSE YR := Year - 1840;
  704.  
  705.     IF MO < 0 THEN BEGIN
  706.       MO := MO+12;
  707.       YR := YR-1;
  708.       END;
  709.  
  710.     JUL := INT ((YR * 1461) / 4);
  711.     JUL := INT ((((153 * MO) + 2) / 5)) + JUL + DA - 306;
  712.     IF JUL > 21609 THEN JUL := JUL - 1;
  713.  
  714.     JUL := JUL * 86400.0;
  715.     Sec := (Hour * 3600.0) + (Minute * 60.0) + Second;
  716.  
  717.     JulSec := JUL + Sec;
  718.     END;
  719. END;
  720.  
  721. PROCEDURE R2Date (JulSec : REAL; VAR DateTime : T_DateTime);
  722. VAR
  723.   NumberSecs : REAL;
  724.   NumberDays : REAL;
  725.   MO,DA,YR,J,W : REAL;
  726.   TempWeekDay  : INTEGER;
  727.  
  728. BEGIN
  729.   NumberDays := INT (JulSec / 86400.0);
  730.   NumberSecs := INT (JulSec - (NumberDays * 86400.0));
  731.  
  732.   WITH DateTime DO BEGIN
  733.     NumSeconds := JulSec;
  734.     NumMinutes := JulSec / 60.0;
  735.     NumHours   := JulSec / 3600.0;
  736.     NumDays    := JulSec / 86400.0;
  737.  
  738.     Hour   := TRUNC (NumberSecs / 3600);
  739.     Minute := TRUNC ((NumberSecs - (Hour * 3600.0)) / 60);
  740.     Second := TRUNC (NumberSecs - (Hour * 3600.0) - (Minute * 60.0));
  741.  
  742.     {Gregorian Date Routine (Leap Centuries are OK)}
  743.     J := NumberDays + 305;
  744.     IF NumberDays > 21608 THEN J := J + 1;
  745.  
  746.     YR := INT ((4 * J + 3) / 1461);
  747.     DA := INT (((J * 4 + 3) - (1461 * YR) + 4) / 4);
  748.     MO := INT ((5 * DA - 3) / 153);
  749.  
  750.     DA := INT ((((5 * DA - 3) - (153 * MO)) + 5) / 5);
  751.     MO := MO + 3;
  752.     YR := YR + 1840;
  753.  
  754.     IF MO > 12 THEN BEGIN
  755.       MO := MO - 12;
  756.       YR := YR + 1;
  757.       END;
  758.  
  759.     Day   := TRUNC (DA);
  760.     Month := TRUNC (MO);
  761.     Year  := TRUNC (YR);
  762.  
  763.     MonthStr := MonthName [Month];
  764.  
  765.     {Find the day of the week}
  766.     TempWeekday := TRUNC (INT (NumberDays - (INT (NumberDays / 7) * 7) - 2));
  767.     IF TempWeekDay < 1 THEN TempWeekDay := TempWeekDay + 7;
  768.     WeekDay := TempWeekDay;
  769.     WeekdayStr := WeekdayName [WeekDay];
  770.     END;
  771. END;
  772.  
  773. FUNCTION Date2S (DateTime : T_DateTime; Mask : STRING) : STRING;
  774.  
  775.   FUNCTION GetFormat (Source : STRING; LookFor : CHAR) : INTEGER;
  776.   VAR
  777.     Ctr : INTEGER;
  778.   BEGIN
  779.     Ctr := POS (LookFor , Source);
  780.     Source := Source + ' ';
  781.     WHILE (Source [Ctr] = LookFor) AND (Ctr < LENGTH (Source)) DO
  782.       Ctr := SUCC (Ctr);
  783.     GetFormat := Ctr - POS (LookFor, Source);
  784.   END;
  785.  
  786. TYPE
  787.   SelectType = ARRAY [2..4] OF STRING[9];
  788.  
  789. VAR
  790.   Ctr      : INTEGER;
  791.   WeekdayFmt,
  792.   MonthFmt,
  793.   DayFmt,
  794.   YearFmt  : INTEGER;
  795.   TempStr  : STRING;
  796.   AmPm     : BOOLEAN;
  797.   TempHour : INTEGER;
  798.   MaskSet  : SET OF CHAR;
  799.   Select   : SelectType;
  800.   Europe   : BOOLEAN;      { YY/DD/MM versus MM/DD/YY }
  801.  
  802.   PROCEDURE Doit (MaskChar  : CHAR; SelectNum : INTEGER);
  803.   BEGIN
  804.     IF SelectNum < 2 THEN SelectNum := 2
  805.     ELSE IF SelectNum > 4 THEN SelectNum := 4;
  806.     WHILE (Ctr < LENGTH (Mask)) AND (NOT (Mask [Ctr] IN MaskSet)) DO BEGIN
  807.       TempStr := TempStr + Mask [Ctr];
  808.       Ctr := SUCC(Ctr);
  809.       END;
  810.  
  811.     IF Mask [Ctr] = MaskChar THEN BEGIN
  812.       TempStr := TempStr + Select [SelectNum];
  813.       Ctr := Ctr + SelectNum;
  814.       END;
  815.   END;
  816.  
  817.   PROCEDURE DoMonth;
  818.   BEGIN
  819.     WITH DateTime DO BEGIN
  820.       Select [2] := I2S  (Month, '@@');
  821.       Select [3] := COPY (MonthStr,1,3);
  822.       Select [4] := MonthStr;
  823.       Doit ('M', MonthFmt);
  824.       MaskSet    := MaskSet - ['M'];
  825.       END;
  826.   END;
  827.  
  828.   PROCEDURE DoDay;
  829.   BEGIN
  830.     WITH DateTime DO BEGIN
  831.       Select [2] := I2S  (Day, '@@');
  832.       Select [3] := 'Error';
  833.       Select [4] := Strip (NumTh (Day), S_Leading);
  834.       Doit ('D', DayFmt);
  835.       MaskSet    := MaskSet - ['D'];
  836.       END;
  837.   END;
  838.  
  839.   PROCEDURE DoYear;
  840.   BEGIN
  841.     WITH DateTime DO BEGIN
  842.       Select [2] := COPY (I2S (Year, '@@@@'),3,2);
  843.       Select [3] := 'Error';
  844.       Select [4] := I2S (Year, '@@@@');
  845.       Doit ('Y', YearFmt);
  846.       MaskSet    := MaskSet - ['Y'];
  847.       END;
  848.   END;
  849.  
  850.   PROCEDURE CheckEuropeDate;
  851.   VAR
  852.     EuropePos : WORD;
  853.   BEGIN
  854.     EuropePos := POS('E',Mask);
  855.     IF EuropePos <> 0 THEN BEGIN
  856.       Europe := TRUE;
  857.       Mask := COPY(Mask,1,EuropePos-1) + COPY(Mask,EuropePos+1,LENGTH(Mask));
  858.       END
  859.     ELSE Europe := FALSE;
  860.   END;
  861.  
  862. BEGIN  { Date2S }
  863.   CheckEuropeDate;
  864.   TempStr    := '';
  865.   WeekdayFmt := 0;
  866.   MonthFmt   := 0;
  867.   DayFmt     := 0;
  868.   YearFmt    := 0;
  869.  
  870.   WeekdayFmt := GetFormat (Mask, 'W');
  871.   MonthFmt   := GetFormat (Mask, 'M');
  872.   DayFmt     := GetFormat (Mask, 'D');
  873.   YearFmt    := GetFormat (Mask, 'Y');
  874.  
  875.   AmPm := FALSE;
  876.  
  877.   {Add a blank the the end of the mask so we don't have to test if Ctr is
  878.    past the end of the string every time.  (Since multiple conditions in IF
  879.    statements don't short circuit, for example:
  880.  
  881.      IF (Ctr < LENGTH (Mask)) AND (Mask [Ctr] = ' ') THEN ..
  882.  
  883.    will still generate a runtime error (with $r+) at the instruction
  884.    Mask [Ctr], if Ctr is greater than the length of the string Mask!)}
  885.  
  886.   Mask := Mask + ' ';
  887.  
  888.   {Search for AmPm indicator}
  889.   FOR Ctr := 1 TO LENGTH (Mask) DO
  890.     CASE Mask [Ctr] OF
  891.       'a' : BEGIN
  892.               IF DateTime.Hour > 11 THEN Mask [Ctr] := 'p';
  893.               AmPm := TRUE;
  894.               END;
  895.       'p' : BEGIN
  896.               IF DateTime.Hour < 12 THEN BEGIN
  897.                 Mask [Ctr] := ' ';
  898.                 IF Mask [SUCC(Ctr)] = 'm' THEN Mask [SUCC(Ctr)] := ' ';
  899.                 END;
  900.               AmPm := TRUE;
  901.               END;
  902.       END;
  903.  
  904.   Ctr  := 1;
  905.  
  906.  
  907.   WITH DateTime DO BEGIN
  908.     MaskSet    := ['W','M','D','Y','h','m','s'];
  909.     Select [2] := 'Error';
  910.     Select [3] := COPY (WeekdayStr,1,3);
  911.     Select [4] := WeekdayStr;
  912.     Doit ('W', WeekdayFmt);
  913.     MaskSet    := MaskSet - ['W'];
  914.  
  915.     IF NOT Europe THEN BEGIN
  916.       DoMonth;
  917.       DoDay;
  918.       DoYear;
  919.       END
  920.     ELSE BEGIN
  921.       DoYear;
  922.       DoMonth;
  923.       DoDay;
  924.       END;
  925.  
  926.     WHILE (Ctr < LENGTH (Mask)) AND (NOT (Mask [Ctr] IN ['h','m','s'])) DO BEGIN
  927.       TempStr := TempStr + Mask[Ctr];
  928.       Ctr := SUCC(Ctr);
  929.       END;
  930.  
  931.     IF (Mask [Ctr] = 'h') AND (Mask [SUCC(Ctr)] = 'h') THEN BEGIN
  932.       TempHour := Hour;
  933.       IF AmPm THEN
  934.         IF      Hour = 0  THEN TempHour := 12
  935.         ELSE IF Hour > 13 THEN TempHour := Hour - 12;
  936.  
  937.       TempStr := TempStr + I2S (TempHour, '@@');
  938.       Ctr := Ctr + 2;
  939.       END;
  940.  
  941.     WHILE (Ctr < LENGTH (Mask)) AND (NOT (Mask [Ctr] IN ['m','s'])) DO BEGIN
  942.       TempStr := TempStr + Mask[Ctr];
  943.       Ctr := SUCC(Ctr);
  944.       END;
  945.  
  946.       IF (Mask [Ctr] = 'm') AND (Mask [SUCC(Ctr)] = 'm') THEN BEGIN
  947.         TempStr := TempStr + I2S (Minute, '@@');
  948.         Ctr := Ctr + 2;
  949.         END;
  950.  
  951.     WHILE (Ctr < LENGTH (Mask)) AND (NOT (Mask [Ctr] IN ['s'])) DO BEGIN
  952.       TempStr := TempStr + Mask[Ctr];
  953.       Ctr := SUCC(Ctr);
  954.       END;
  955.  
  956.     IF (Mask [Ctr] = 's') AND (Mask [SUCC(Ctr)] = 's') THEN BEGIN
  957.       TempStr := TempStr + I2S (Second, '@@');
  958.       Ctr := Ctr + 2;
  959.       END;
  960.     END;
  961.  
  962.     IF Ctr < LENGTH (Mask) THEN
  963.       REPEAT
  964.         TempStr := TempStr + Mask[Ctr];
  965.         Ctr := SUCC(Ctr);
  966.         UNTIL Ctr > LENGTH (Mask) - 1;
  967.  
  968.   Date2S := TempStr;
  969. END;
  970.  
  971. PROCEDURE GetDOSDateAndTime (VAR JulSec : REAL; VAR DateTime : T_DateTime);
  972.  
  973. VAR
  974.   DosReg : Registers;
  975.  
  976. BEGIN
  977.   WITH DosReg, DateTime DO BEGIN
  978.     AH := $2A;  {DOS Date}
  979.     AL := $00;
  980.     INTR (_DOS,DosReg);
  981.  
  982.     WeekDay    := AL+1;
  983.     WeekdayStr := WeekdayName [AL+1]; { DOS returns the week day in AL,}
  984.     MonthStr   := MonthName   [DH];
  985.     Year       := CX;                 { the year in CX,                }
  986.     Month      := DH;                 { the month in DH,               }
  987.     Day        := DL;                 { and the day in DL.             }
  988.  
  989.     AH := $2C;  {DOS Time}
  990.     AL := $00;
  991.     INTR (_DOS, DosReg);
  992.  
  993.     Hour   := CH;            { DOS returns the hours in CH,   }
  994.     Minute := CL;            { the minutes in CL,             }
  995.     Second := DH;            { the seconds in DH, and the     }
  996.     END;
  997.  
  998.   {Update NumDays, NumHours, NumMinutes, and NumSeconds}
  999.   Date2R (JulSec, DateTime);
  1000.   R2Date (JulSec, DateTime);
  1001. END;
  1002.  
  1003. PROCEDURE SetDOSDateAndTime (DateTime : T_DateTime);
  1004.  
  1005. VAR
  1006.   DosReg : Registers;
  1007.  
  1008. BEGIN
  1009.   WITH DosReg, DateTime DO BEGIN
  1010.     CX := Year;  { the year in CX,    }
  1011.     DH := Month; { the month in DH,   }
  1012.     DL := Day;   { and the day in DL. }
  1013.  
  1014.     AH := $2B;  {Set DOS Date}
  1015.     AL := $00;
  1016.     INTR (_DOS,DosReg);
  1017.  
  1018.     CH := Hour;   { the hours in CH,   }
  1019.     CL := Minute; { the minutes in CL, }
  1020.     DH := Second; { the seconds in DH. }
  1021.  
  1022.     AH := $2D;  {Set DOS Time}
  1023.     AL := $00;
  1024.     INTR (_DOS, DosReg);
  1025.     END;
  1026. END;
  1027.